package com.github.shiqiyue.blog.markdown.config.web.security;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.redisson.api.RBucket;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import com.github.shiqiyue.blog.markdown.utils.json.JsonUtils;

/***
 * 持久化token仓库,基于redisson
 * 
 * @author wwy
 *
 */
public class RedissonTokenRepositoryImpl implements PersistentTokenRepository {
	
	/***
	 * redisson客户端
	 */
	private RedissonClient redissonClient;
	
	/***
	 * token的redis key前缀
	 */
	private String redisKeyPrefix = "redisson_spring_session_rememberme:";
	
	/***
	 * 用户名的redis key 前缀
	 */
	private String userTokenListRedisKeyPrefix = "redisson_spring_session_rememberme:user:";
	
	/***
	 * token的过期时间
	 */
	private Long tokenTimeOut = 604800L;
	
	public RedissonTokenRepositoryImpl(RedissonClient redissonClient) {
		this.setRedissonClient(redissonClient);
	}
	
	@Override
	public void createNewToken(PersistentRememberMeToken token) {
		RBucket<String> rbucketToken = getRedissonClient().getBucket(getRedisKeyPrefix() + token.getSeries());
		rbucketToken.set(JsonUtils.beanToString(token), getTokenTimeOut(), TimeUnit.SECONDS);
		RList<String> userTokenList = getRedissonClient().getList(userTokenListRedisKeyPrefix + token.getUsername());
		userTokenList.add(token.getSeries());
	}
	
	@Override
	public void updateToken(String series, String tokenValue, Date lastUsed) {
		RBucket<String> rbucketToken = getRedissonClient().getBucket(getRedisKeyPrefix() + series);
		if (!rbucketToken.isExists()) {
			throw new TokenNotFoundException("更新token错误,redis中没有token数据");
		}
		PersistentRememberMeToken token = JsonUtils.stringToBean(rbucketToken.get(), PersistentRememberMeToken.class);
		PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), series, tokenValue,
				new Date());
		rbucketToken.set(JsonUtils.beanToString(newToken), getTokenTimeOut(), TimeUnit.SECONDS);
	}
	
	@Override
	public PersistentRememberMeToken getTokenForSeries(String seriesId) {
		RBucket<String> rbucketToken = getRedissonClient().getBucket(getRedisKeyPrefix() + seriesId);
		if (!rbucketToken.isExists()) {
			return null;
		}
		return JsonUtils.stringToBean(rbucketToken.get(), PersistentRememberMeToken.class);
	}
	
	@Override
	public void removeUserTokens(String username) {
		RList<String> userTokenList = getRedissonClient().getList(userTokenListRedisKeyPrefix + username);
		if (userTokenList == null || userTokenList.isEmpty()) {
			return;
		}
		for (String seriesId : userTokenList) {
			RBucket<String> rbucketToken = getRedissonClient().getBucket(getRedisKeyPrefix() + seriesId);
			if (rbucketToken.isExists()) {
				rbucketToken.delete();
			}
		}
	}
	
	public RedissonClient getRedissonClient() {
		return redissonClient;
	}
	
	public void setRedissonClient(RedissonClient redissonClient) {
		this.redissonClient = redissonClient;
	}
	
	public String getRedisKeyPrefix() {
		return redisKeyPrefix;
	}
	
	public void setRedisKeyPrefix(String redisKeyPrefix) {
		this.redisKeyPrefix = redisKeyPrefix;
	}
	
	public Long getTokenTimeOut() {
		return tokenTimeOut;
	}
	
	public void setTokenTimeOut(Long tokenTimeOut) {
		this.tokenTimeOut = tokenTimeOut;
	}
	
	public String getUserTokenListRedisKeyPrefix() {
		return userTokenListRedisKeyPrefix;
	}
	
	public void setUserTokenListRedisKeyPrefix(String userTokenListRedisKeyPrefix) {
		this.userTokenListRedisKeyPrefix = userTokenListRedisKeyPrefix;
	}
	
	/***
	 * token不存在exception
	 * 
	 * @author wwy
	 *
	 */
	public static class TokenNotFoundException extends RuntimeException {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 6848507089697133446L;
		
		public TokenNotFoundException() {
			super();
		}
		
		public TokenNotFoundException(String message, Throwable cause, boolean enableSuppression,
				boolean writableStackTrace) {
			super(message, cause, enableSuppression, writableStackTrace);
		}
		
		public TokenNotFoundException(String message, Throwable cause) {
			super(message, cause);
		}
		
		public TokenNotFoundException(String message) {
			super(message);
		}
		
		public TokenNotFoundException(Throwable cause) {
			super(cause);
		}
		
	}
	
}
